unit dorm.Mappings;

interface

uses
  Rtti,
  Generics.Collections,
  superobject;

type
  // Mapping Attibutes
  Entity = class(TCustomAttribute)
  private
    FTableName: String;
  public
    constructor Create(const TableName: String = '');
    property TableName: String read FTableName;
  end;

  Column = class(TCustomAttribute)
  private
    FColumnName: String;
  public
    constructor Create(const ColumnName: String);
    property ColumnName: String read FColumnName;
  end;

  PrimaryKey = class(TCustomAttribute);
  Transient = class(TCustomAttribute);

  // Mapping classes

  TdormIndexType = (itNone, itIndex, itUnique);
  TdormKeyType = (ktInteger, ktString);

  TMappingField = class
  private
    FPK: boolean;
    FName: string;
    FFieldType: string;
    FDefaultValue: string;
    FIndexType: TdormIndexType;
    FPrecision: Cardinal;
    FFieldName: string;
    FSize: Cardinal;
  public
    procedure Assign(Source: TMappingField);
    function ToString: string; override;
    property Name: string read FName write FName;
    property FieldName: string read FFieldName write FFieldName;
    property FieldType: string read FFieldType write FFieldType;
    property DefaultValue: string read FDefaultValue write FDefaultValue;
    property Size: Cardinal read FSize write FSize;
    property Precision: Cardinal read FPrecision write FPrecision;
    property IndexType: TdormIndexType read FIndexType write FIndexType;
    property IsPK: boolean read FPK write FPK;
  end;

  TMappingRelation = class
  private
    FName: string;
    FChildClassName: string;
    FChildFieldName: string;
    FLazyLoad: boolean;
  public
    property Name: string read FName write FName;
    property ChildClassName: string read FChildClassName write FChildClassName;
    property ChildFieldName: string read FChildFieldName write FChildFieldName;
    property LazyLoad: boolean read FLazyLoad write FLazyLoad;
    procedure Assign(Source: TMappingRelation);
  end;

  TMappingBelongTo = class
  private
    FName: string;
    FOwnerClassName: string;
    FRefFieldName: string;
    FLazyLoad: boolean;
  public
    property Name: string read FName write FName;
    property OwnerClassName: string read FOwnerClassName write FOwnerClassName;
    property RefFieldName: string read FRefFieldName write FRefFieldName;
    property LazyLoad: boolean read FLazyLoad write FLazyLoad;
    procedure Assign(Source: TMappingBelongTo);
  end;

  TMappingFieldList = class(TObjectList<TMappingField>);
  TMappingRelationList = class(TObjectList<TMappingRelation>);
  TMappingBelongToList = class(TObjectList<TMappingBelongTo>);

  TMappingTable = class
  private
    FIdType: TdormKeyType;
    FPackage: string;
    FTableName: string;
    FFields: TMappingFieldList;
    FBelongToList: TMappingBelongToList;
    FHasManyList: TMappingRelationList;
    FHasOneList: TMappingRelationList;
    FIdNullValue: TValue;
    function GetId: TMappingField;
  public
    constructor Create;
    destructor Destroy; override;
    function AddField: TMappingField;
    function AddBelongTo: TMappingBelongTo;
    function AddHasMany: TMappingRelation;
    function AddHasOne: TMappingRelation;
    function ToString: string; override;
    property Package: string read FPackage write FPackage;
    property TableName: string read FTableName write FTableName;
    property Id: TMappingField read GetId;
    property IdType: TdormKeyType read FIdType write FIdType;
    property IdNullValue: TValue read FIdNullValue write FIdNullValue;
    property Fields: TMappingFieldList read FFields;
    property HasManyList: TMappingRelationList read FHasManyList;
    property HasOneList: TMappingRelationList read FHasOneList;
    property BelongToList: TMappingBelongToList read FBelongToList;
  end;

implementation

uses
  SysUtils, StrUtils, dorm.Utils;

{ Entity }

constructor Entity.Create(const TableName: String);
begin
  FTableName := TableName;
end;

{ Column }

constructor Column.Create(const ColumnName: String);
begin
  FColumnName := ColumnName;
end;

{ TMappingTable }

constructor TMappingTable.Create;
begin
  FFields := TMappingFieldList.Create;
  FHasOneList := TMappingRelationList.Create;
  FHasManyList := TMappingRelationList.Create;
  FBelongToList := TMappingBelongToList.Create;
end;

destructor TMappingTable.Destroy;
begin
  FFields.Free;
  FHasManyList.Free;
  FHasOneList.Free;
  FBelongToList.Free;
  inherited;
end;

function TMappingTable.GetId: TMappingField;
var
  F: TMappingField;
begin
  Result := Nil;

  for F in Fields do
    if F.IsPK then
      Exit(F);
end;

function TMappingTable.AddBelongTo: TMappingBelongTo;
begin
  Result := TMappingBelongTo.Create;
  FBelongToList.Add(Result);
end;

function TMappingTable.AddField: TMappingField;
begin
  Result := TMappingField.Create;
  FFields.Add(Result);
end;

function TMappingTable.AddHasMany: TMappingRelation;
begin
  Result := TMappingRelation.Create;
  FHasManyList.Add(Result);
end;

function TMappingTable.AddHasOne: TMappingRelation;
begin
  Result := TMappingRelation.Create;
  FHasOneList.Add(Result);
end;

function TMappingTable.ToString: string;
begin
  Result := Format('table: %s, key type: %s, null key value: %s',
    [TableName, Integer(IdType), IdNullValue.AsString]);
end;

{ TMappingField }

procedure TMappingField.Assign(Source: TMappingField);
begin
  FPK := Source.IsPK;
  FName := Source.Name;
  FFieldType := Source.FieldType;
  FDefaultValue := Source.DefaultValue;
  FIndexType := Source.IndexType;
  FPrecision := Source.Precision;
  FFieldName := Source.FieldName;
  FSize := Source.Size;
end;

function TMappingField.ToString: string;
begin
  Result := Format('PK: %s, name: %s, field: %s, field type: %s',
    [BoolToStr(IsPK, True), name, FieldName, FieldType]);
end;

{ TMappingRelation }

procedure TMappingRelation.Assign(Source: TMappingRelation);
begin
  FName := Source.Name;
  FChildClassName := Source.ChildClassName;
  FChildFieldName := Source.ChildFieldName;
  FLazyLoad := Source.LazyLoad;
end;

{ TMappingBelongTo }

procedure TMappingBelongTo.Assign(Source: TMappingBelongTo);
begin
  FName := Source.Name;
  FOwnerClassName := Source.OwnerClassName;
  FRefFieldName := Source.RefFieldName;
  FLazyLoad := Source.LazyLoad;
end;

end.
